<?php
/**
 * A static toolkit to get and create rail_object Objects.
 *
 * PHP versions 5
 *
 * Copyright (C)
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of ERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software
 * Fundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
 *
 * @author Bastian Schröder <bs@chemanet.de>
 * @version 2007-12-14
 * @package rail
 */
class rail_factory {


	/**
	 * Create a new rail_container.
	 *
	 * @param rail_connector $pRailConnector
	 * @param rail_object $pEnvironment
	 * @param string $pName OBJ_NAME (optional)
	 * @return rail_container
	 */
	public static function create_container ( $pRailConnector, $pEnvironment, $pName = 'new_container' ) {
		return rail_factory::create_object( $pRailConnector, $pEnvironment, $pName , 'RAIL_CONTAINER' );
	}


	/**
	 * Create a new rail_document. If requested set content and mimetye for created rail_document.
	 *
	 * @param rail_connector $pRailConnector
	 * @param rail_object $pEnvironment
	 * @param string $pName OBJ_NAME (optional)
	 * @param string $pDocument (optional)
	 * @param string $pMimetype (optional)
	 * @return rail_document
	 */
	public static function create_document ( $pRailConnector, $pEnvironment, $pName = 'new_document', $pDocument = null, $pMimetype = null ) {
		$object = rail_factory::create_object( $pRailConnector, $pEnvironment, $pName , 'RAIL_DOCUMENT' );

		// Set documents content
		if( $pDocument != null && strlen( trim( $pDocument ) ) > 0 )
			$object->set_content( $pDocument, $pMimetype );

		return $object;
	}


	/**
	 * Create a new rail_group and set the group to created rail_group itself.
	 *
	 * @param rail_connector $pRailConnector
	 * @param rail_object $pEnvironment
	 * @param string $pName OBJ_NAME (optional)
	 * @return rail_group
	 */
	public static function create_group ( $pRailConnector, $pEnvironment, $pName = 'new_group' ) {
		$object = rail_factory::create_object( $pRailConnector, $pEnvironment, $pName , 'RAIL_GROUP' );

		// set group to new group
		$pRailConnector->send_request( "UPDATE objects SET `group`='" . $object->get_id() . "' WHERE id='" . $object->get_id() . "' LIMIT 1" );

		return $object;
	}


	/**
	 * Create a new create_link. If requested set link target for created rail_link
	 *
	 * @param rail_connector $pRailConnector
	 * @param rail_object $pEnvironment
	 * @param string $pName OBJ_NAME (optional)
	 * @param rail_object $pRailObject (optional)
	 * @return rail_link
	 */
	public static function create_link ( $pRailConnector, $pEnvironment, $pName = 'new_link', $pRailObject = null ) {
		$object = rail_factory::create_object( $pRailConnector, $pEnvironment, $pName , 'RAIL_LINK' );

		// set link
		if( $pRailObject instanceof rail_object )
			$object->set_link( $pRailObject );

		return $object;
	}


	/**
	 * Create a new rail_object. By default the type of the created rail_object will be RAIL_OBJECT, but it's possible
	 * to set the type to a user defined a rail object class.
	 *
	 * @param rail_connector $pRailConnector
	 * @param rail_object $pEnvironment
	 * @param string $pName OBJ_NAME (optional)
	 * @param string $pRailType (optional)
	 * @return rail_object
	 */
	public static function create_object ( $pRailConnector, $pEnvironment, $pName = 'new', $pRailType = 'rail_object' ) {
		// check wheather environment exist
		if( !$pEnvironment instanceof rail_object )
			throw new rail_param_exception( null, 'pEnvironment', $pEnvironment, 'environment isn\'t a valid rail_object.' );
		// check wheather environment is writeable
		if( !$pEnvironment->is_writeable() )
		 	throw new rail_writepermission_exception( $pEnvironment, $pRailConnector->get_user() );

		$timestamp = time();
		$name = strlen( trim( $pName ) ) > 0 ? trim( $pName ) : 'new';


		// Set object
		$id = $pRailConnector->send_request( "
			INSERT INTO
				objects ( `type`, `environment`, `user`, `group`, `mode` )
			VALUES
				(
					'" . strtoupper($pRailType) . "',
					'" . $pEnvironment->get_id() . "',
					'" . $pRailConnector->get_user()->get_id() . "',
					'" . $pRailConnector->get_user()->get_group()->get_id() . "',
					'110100000'
				)
	 	");

		// Set OBJ_NAME, OBJ_TIME_CREATE and OBJ_TIME_LASTWRITE
		$pRailConnector->send_request( "
			INSERT INTO objects_attributes (`object`,`attribute`,`value`) VALUES ('$id','OBJ_NAME','$name');
			INSERT INTO objects_attributes (`object`,`attribute`,`value`) VALUES ('$id','OBJ_TIME_CREATE','$timestamp');
			INSERT INTO objects_attributes (`object`,`attribute`,`value`) VALUES ('$id','OBJ_TIME_LASTWRITE','$timestamp')
		" );

		return rail_factory::get_object( $pRailConnector, $id );
	}


	/**
	 * Create a new rail_sequence. If requested push some rail_object Objects to created rail_sequence.
	 *
	 * @param rail_connector $pRailConnector
	 * @param rail_object $pEnvironment
	 * @param string $pName OBJ_NAME (optional)
	 * @param mixed $pRailObjects a rail_object or a array of rail_object Objects (optional).
	 * @return rail_sequence
	 */
	public static function create_sequence ( $pRailConnector, $pEnvironment, $pName = 'new_sequence', $pRailObjects = null ) {
		$object = rail_factory::create_object( $pRailConnector, $pEnvironment, $pName , 'RAIL_SEQUENCE' );

		if( $pRailObjects != null )
			$object->push( $pRailObjects );

		return $object;
	}


	/**
	 * Create a new rail_user. A valid rail_user must has a unique name and a password.
	 *
	 * @exception rail_exception
	 * @param rail_connector $pRailConnector
	 * @param rail_object $pEnvironment
	 * @param string $pName OBJ_NAME
	 * @param string $pPasswd RAIL_PASSWD
	 * @return rail_user
	 */
	public static function create_user ( $pRailConnector, $pEnvironment, $pName = '', $pPasswd = '' ) {
		// check wheather name is empty
		if( strlen( trim( $pName) ) == 0 )
			throw new rail_exception( null, 'Cany\'t create new user without a name!' );
		// check wheather passwd is empty
		if( strlen( trim( $pPasswd) ) == 0 )
			throw new rail_exception( null, 'Can\'t create new user without a password!' );
		// check wheather the name already exist
		$data = $pRailConnector->send_request( "
			SELECT
				objects.`id`, objects_attributes.`value` as `name`
			FROM
				objects,objects_attributes
			WHERE
				objects.`id`=objects_attributes.`object` AND objects_attributes.`attribute`='OBJ_NAME' AND objects.`type`='RAIL_USER'
		" );
		while( $data_row = $data->fetch_assoc() ){
			if( $data_row['name'] != $pName )
				continue;

			throw new rail_rail_exception( null, "Can't create user: $pName already exist!" );
		}

		$object = rail_factory::create_object( $pRailConnector, $pEnvironment, $pName , 'RAIL_USER' );

		// set user to created user
		$pRailConnector->send_request( "
			UPDATE objects SET `user`='" . $object->get_id() . "' WHERE id='" . $object->get_id() . "' LIMIT 1;
			UPDATE objects SET `mode`='110110000' WHERE id='" . $object->get_id() . "' LIMIT 1;
			INSERT INTO objects_attributes (`object`,`attribute`,`value`) VALUES ('" . $object->get_id() . "','RAIL_PASSWD','" . rtrim( md5( $pPasswd ) ) . "')
		" );

		return $object;
	}


	/**
	 * Get object from RAIL. Requires a valid rail_id or a valid rail_path. If a PHP class file exist with rail_object
	 * Objects type return this class. Otherwise return a rail_object.
	 *
	 * @exception rail_exception
	 * @param rail_connector $pRailConnector
	 * @param mixed $pObject a rail_id or a rail_path.
	 * @return mixed
	 */
	public static function get_object ( $pRailConnector, $pObject ) {
		// locate objects rail_id if pObject is a rail_path
		if( !is_numeric($pObject) ){
			$path = trim( $pObject );
			// remove last slash, if exist
			if( substr( $path, - 1 ) == '/' )
				$path = substr( $path, 0, strlen($path) - 1 );
			// explode path to array and remove first array object (root path)
			$path = explode( '/', $path );
			unset( $path[0] );
			// return rail_root if path is a valid path to rail_root
			if( count($path) == 0 )
				return $pRailConnector->get_root();
			else{
				// for each path do
				$environment = $pRailConnector->get_root()->get_id();
				foreach( $path as $tracker ){
					$object_ids = array();
					$name = substr($tracker, 0, strrpos( $tracker, '[' )?strrpos( $tracker, '[' ):strlen($tracker) );
					// search for name in current inventory
					$data = $pRailConnector->send_request(
						"SELECT objects.id
						 FROM objects,objects_attributes
						 WHERE objects.id = objects_attributes.object AND objects.environment ='$environment' AND objects_attributes.`attribute` = 'OBJ_NAME' AND objects_attributes.`value` = '" . urldecode( $name ) . "' ORDER BY `id`"
					);
					// store all founded objects as id in an array
					$possible_objects = array();
					while( $data_row = $data->fetch_assoc() )
						$possible_objects[] = $data_row['id'];
					// get next environment
					if( count($possible_objects) <= 0 )
						throw new rail_exception( null, 'Object dosn\'t exist!' );
					$environment = $possible_objects[ (strrpos( $tracker, '[' ) ? substr( $tracker, strrpos( $tracker, '[' ) + 1, strlen( $tracker ) - strrpos( $tracker, '[' ) - 2 ) - 1 : 0) ];
				}
				// last founded environment is the requested object id
				$pObject = $environment;
			}
		}

		// try to finger out the type for the RAIL object.
		$data = $pRailConnector->send_request( "SELECT `type` FROM objects WHERE `id`='$pObject' LIMIT 1" )->fetch_assoc();

		// throw an exception, if the requested rail_object object dosn't exist.
		if( count( $data ) <= 0 )
			throw new rail_exception( null, 'Object dosn\'t exist!' );

		// return rail_object
		$class = class_exists( $data[ 'type' ] ) ? $data[ 'type' ] : 'rail_object';
		return new $class( $pRailConnector, $pObject );
	}


	/**
	 * Return rail_object if object exist otherwise return false.
	 *
	 * @param rail_connector $pRailConnector
	 * @param mixed $pObject a rail_id or a rail_path.
	 * @return mixed
	 */
	public static function is_object ( $pRailConnector, $pObject ) {
		try{
			return rail_factory::get_object( $pRailConnector, $pObject );
		}
		catch( rail_exception $e ){ return false; }
	}


	/**
	 * *alpha*
	 * Search for objects by attributes or types. Return array of rail_objects.
	 *
	 * @param rail_connector $pRailConnector
	 * @param array $pMatchAttributes (not supported yet)
	 * @param string $pRailTypes (optional)
	 * @return array
	 */
	public static function search ( $pRailConnector, $pMatchAttributes, $pRailTypes = 'ALL' ) {
		// build SQL WHERE
		$sql_where_types = '';
		if( strlen($pRailTypes) > 0 && $pRailTypes != 'ALL' ){
			foreach( explode('|', $pRailTypes) as $type )
				$sql_where_types .= " OR `type`='$type'";

			$sql_where_types = ' AND(' . substr($sql_where_types, 4) . ')';
		}

		// search for objects
		$data = $pRailConnector->send_request("
			SELECT DISTINCT objects.id
			FROM objects,objects_attributes
			WHERE objects.id = objects_attributes.object
			$sql_where_types
		");

		// store all objects from search result into return array
		$objects = array();
		while( $data_row = $data->fetch_assoc() )
			$objects[] = rail_factory::get_object( $pRailConnector, $data_row['id'] );

		return $objects;
	}

} // End of rail_factory
?>